#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "malha.h"
#include "assembler.h"

//cria��o e inicializa��o das vari�veis
GLfloat angle, fAspect;
GLfloat olho [] = {0, 0, 0};
GLfloat cima [] = {0, 1, 0};
GLfloat angle_rot1 = 0, angle_rot2 = 0, angle_rot3 = 0;
float xm = 0, ym = 0, zm = 0;
int k, l, m, n, girando = 0, zoom = 0;

void Desenha();
void Inicializa();
void AlteraTamanhoJanela(GLsizei w, GLsizei h);
void GerenciaMouse(int button, int state, int x, int y);
void keyboard_callback_special(int key, int x, int y);

// Fun��o callback chamada para fazer o desenho

void Desenha(void) {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glScalef(0.5,0.5, 0.5);

    glPushMatrix();

    sqsLoadIdentity(PROJECTIONMATRIX);
    sqsLoadIdentity(MODELMATRIX);
    sqsLoadIdentity(VIEWMATRIX);
    sqsPerspective(angle, fAspect, 0.1, 500);

    VECTOR3 center, up;
    sqsSETVECTOR3(center, 0, 0, 0);
    sqsSETVECTOR3(up, cima[0], cima[1], cima[2]);
    sqsViewer(olho[0], olho[1], olho[2], center, up);

    /////////////////////////////////////////////////////////
    /////Mostra o coelhinho do Davi*/
    sqsRotateX(MODELMATRIX, angle_rot1);
    sqsRotateY(MODELMATRIX, angle_rot2);
    sqsRotateZ(MODELMATRIX, angle_rot3);
    sqsTranslate(MODELMATRIX, -xm, -ym, -zm);

    sqsColor3f(0, 0, 1);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    //desenha a Bounding Box
    sqsAssemblyMode(SQS_LINE_STRIP);
    sqsPushVertex3f(minx, miny, minz);
    sqsPushVertex3f(maxx, miny, minz);
    sqsPushVertex3f(maxx, maxy, minz);
    sqsPushVertex3f(minx, maxy, minz);
    sqsPushVertex3f(minx, miny, minz);
    sqsAssemblyMode(SQS_NONE);

    sqsAssemblyMode(SQS_LINE_STRIP);
    sqsPushVertex3f(maxx, maxy, maxz);
    sqsPushVertex3f(maxx, maxy, minz);
    sqsPushVertex3f(maxx, miny, minz);
    sqsPushVertex3f(maxx, miny, maxz);
    sqsPushVertex3f(maxx, maxy, maxz);
    sqsAssemblyMode(SQS_NONE);

    sqsAssemblyMode(SQS_LINE_STRIP);
    sqsPushVertex3f(maxx, maxy, maxz);
    sqsPushVertex3f(minx, maxy, maxz);
    sqsPushVertex3f(minx, maxy, minz);
    sqsPushVertex3f(maxx, maxy, minz);
    sqsPushVertex3f(maxx, maxy, maxz);
    sqsAssemblyMode(SQS_NONE);

    sqsAssemblyMode(SQS_LINE_STRIP);
    sqsPushVertex3f(minx, miny, minz);
    sqsPushVertex3f(maxx, miny, minz);
    sqsPushVertex3f(maxx, miny, maxz);
    sqsPushVertex3f(minx, miny, maxz);
    sqsPushVertex3f(minx, miny, minz);
    sqsAssemblyMode(SQS_NONE);

    sqsAssemblyMode(SQS_LINE_STRIP);
    sqsPushVertex3f(maxx, maxy, maxz);
    sqsPushVertex3f(minx, maxy, maxz);
    sqsPushVertex3f(minx, miny, maxz);
    sqsPushVertex3f(maxx, miny, maxz);
    sqsPushVertex3f(maxx, maxy, maxz);
    sqsAssemblyMode(SQS_NONE);

    sqsAssemblyMode(SQS_LINE_STRIP);
    sqsPushVertex3f(minx, miny, minz);
    sqsPushVertex3f(minx, miny, maxz);
    sqsPushVertex3f(minx, maxy, maxz);
    sqsPushVertex3f(minx, maxy, minz);
    sqsPushVertex3f(minx, miny, minz);
    sqsAssemblyMode(SQS_NONE);

    // Desenha a malha
    sqsAssemblyMode(SQS_TRIANGLE);
    {
        int t;
        for (t = 0; t < ntriangles; t++) {
            HE_EDGE* e = triangulos[t].edge;
            do {
                sqsColor3f((-minx + e->vert->x) / (maxx - minx), (-miny + e->vert->y) / (maxy - miny), (minz + e->vert->z) / (maxz - minz));
                sqsPushVertex3f(e->vert->x, e->vert->y, e->vert->z);
                e = e->next;
            } while (e != triangulos[t].edge);
        }
    }
    sqsAssemblyMode(SQS_NONE);

    //////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////

    sqsAssemblyMode(SQS_LINES);
    sqsColor3f(1, 0, 0);
    sqsPushVertex3f(xm, ym, zm);
    sqsPushVertex3f(maxx, ym, zm);

    sqsColor3f(0, 1, 0);
    sqsPushVertex3f(xm, ym, zm);
    sqsPushVertex3f(xm, maxy, zm);

    sqsColor3f(0, 0, 1);
    sqsPushVertex3f(xm, ym, zm);
    sqsPushVertex3f(xm, ym, maxz);
    sqsAssemblyMode(SQS_NONE);

    glPopMatrix();
    // Executa os comandos OpenGL
    glutSwapBuffers();
}

// Inicializa par�metros de rendering

void Inicializa(void) {
    int dmax;
    int i, j;


    glShadeModel(GL_SMOOTH);
    /*
    for (i = 0; i <= nhalfedges; i++) {
      for (j = 0; j <= nhalfedges; j++) {
        if ((arestas[j].vert == arestas[i].next->vert) && (arestas[i].vert == arestas[j].next->vert)) {
     *(arestas[j].pair) = arestas[i];
     *(arestas[i].pair) = arestas[j];
          break;
        }
      }
    }*/

    xm = (maxx + minx) / 2;
    ym = (maxy + miny) / 2;
    zm = (maxz + minz) / 2;

    if (fabs(maxx - minx) > fabs(maxy - miny)) dmax = fabs(maxx - minx);
    else dmax = fabs(maxy - miny);

    olho[2] = dmax / (2 * tan(M_PI / 8));

    glEnable(GL_DEPTH_TEST);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    angle = 45;
    glPolygonMode(GL_FRONT_AND_BACK, GL_POLYGON);
}

// Fun��o callback chamada quando o tamanho da janela � alterado

void AlteraTamanhoJanela(GLsizei w, GLsizei h) {
    // Para previnir uma divis�o por zero
    if (h == 0) h = 1;

    // Especifica o tamanho da viewport
    glViewport(0, 0, w, h);

    // Calcula a corre��o de aspecto
    fAspect = (GLfloat) w / (GLfloat) h;

}

void GerenciaMovimentoMouse(int x, int y) {
    if (girando) {
        angle_rot2 += (x - k);
        angle_rot1 += (y - l);
        k = x;
        l = y;
        glutPostRedisplay();
    }
    if (zoom) {
        if (x > m) angle_rot3 += (x - m);
        else if (x < m) angle_rot3 -= (m - x);
        if (y > n) {
            olho[0] += olho[0]*0.1 * (y - n) / 10;
            olho[1] += olho[1]*0.1 * (y - n) / 10;
            olho[2] += olho[2]*0.1 * (y - n) / 10;
        } else if (y < n) {
            olho[0] -= olho[0]*0.1 * (n - y) / 10;
            olho[1] -= olho[1]*0.1 * (n - y) / 10;
            olho[2] -= olho[2]*0.1 * (n - y) / 10;
        }
        m = x;
        n = y;
        glutPostRedisplay();
    }
}

// Fun��o callback chamada para gerenciar eventos do mouse

void GerenciaMouse(int button, int state, int x, int y) {
    if (button == GLUT_LEFT_BUTTON) {
        if (state == GLUT_DOWN) {
            k = x;
            l = y;
            girando = 1;
        }
        if (state == GLUT_UP) {
            girando = 0;
        }
    }


    if (button == GLUT_RIGHT_BUTTON) {
        if (state == GLUT_DOWN) {
            m = x;
            n = y;
            zoom = 1;
        }
        if (state == GLUT_UP) {
            zoom = 0;
        }
    }
    glutPostRedisplay();
}

// Fun��o callback chamada para gerenciar eventos do teclado

void keyboard_callback_special(int key, int x, int y) {

    switch (key) {
        case 1:
            angle_rot1 -= 0.1;
            break;

        case 2:
            angle_rot1 += 0.1;
            break;

        case 3:
            angle_rot2 -= 0.1;
            break;

        case 4:
            angle_rot2 += 0.1;

            break;

        case 5:
            angle_rot3 -= 0.1;
            break;

        case 6:
            angle_rot3 += 0.1;
            break;
        default:
            break;
    }
    glutPostRedisplay();

    return;
}

// Programa Principal

int main(int argc, char** argv) {
    //  int i;
    if (argc == 1) argv[1] = "bunny.ply";
    CarregaMalhaPLY(argv);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Visualizacao de Malhas PLY");
    glutDisplayFunc(Desenha);
    glutReshapeFunc(AlteraTamanhoJanela);
    glutMouseFunc(GerenciaMouse);
    glutMotionFunc(GerenciaMovimentoMouse);
    glutSpecialFunc(keyboard_callback_special);
    Inicializa();
    glutMainLoop();
    LiberaMalha();
}


